//
// Created by Administrator on 2021/8/27.
//

#ifndef OPENGLLEARN_TEXTUREAPP_H
#define OPENGLLEARN_TEXTUREAPP_H

#include "Application/Application.h"
#include "Shader/ShaderProgram.h"

#include "FileUtils.h"

class TextureApp : public Application
{
protected:
    void OnCreate() override
    {
        static const GLfloat vertices[] = {
                -0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
                0.5f, -0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
                -0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
                0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
        };

        static const GLuint indices[] = {
                0, 1, 2,
                2, 1, 3
        };

        auto si = ShaderInfo("../../Chapter2/res/tex_vert.glsl",
                             "../../Chapter2/res/tex_frag.glsl");
        sp_ = ShaderProgram(si);

        glCreateVertexArrays(1, &vao_);
        glBindVertexArray(vao_);

        glCreateBuffers(1, &ebo_);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo_);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof indices, indices, GL_STATIC_DRAW);

        glCreateBuffers(1, &vbo_);
        glBindBuffer(GL_ARRAY_BUFFER, vbo_);
        glBufferData(GL_ARRAY_BUFFER, sizeof vertices, vertices, GL_STATIC_DRAW);

        // positions
        glVertexArrayAttribBinding(vao_, 0, 0);
        glVertexArrayAttribFormat(vao_, 0, 3, GL_FLOAT, GL_FALSE, 0);
        glVertexArrayVertexBuffer(vao_, 0, vbo_, 0, 8 * sizeof(GLfloat));
        glEnableVertexArrayAttrib(vao_, 0);
        // colors
        glVertexArrayAttribBinding(vao_, 1, 1);
        glVertexArrayAttribFormat(vao_, 1, 3, GL_FLOAT, GL_FALSE, 0);
        glVertexArrayVertexBuffer(vao_, 1, vbo_, 3 * sizeof(GLfloat), 8 * sizeof(GLfloat));
        glEnableVertexArrayAttrib(vao_, 1);
        // uvs
        glVertexArrayAttribBinding(vao_, 2, 2);
        glVertexArrayAttribFormat(vao_, 2, 2, GL_FLOAT, GL_FALSE, 0);
        glVertexArrayVertexBuffer(vao_, 2, vbo_, 6 * sizeof(GLfloat), 8 * sizeof(GLfloat));
        glEnableVertexArrayAttrib(vao_, 2);

        // textures
        GLubyte* imageData = nullptr;
        int imageW, imageH, channels;
        FileUtils::LoadImage("../../Chapter2/res/02.jpg", &imageData, &imageW, &imageH, &channels);
//        imageData = new GLubyte[256 * 256 * 4];
//        auto p = imageData;
//        imageW = 256;
//        imageH = 256;
//        for(auto i = 0; i < 256; ++i)
//        {
//            for(auto j = 0; j < 256; ++j)
//            {
//                *(p++) = 255;
//                *(p++) = 255;
//                *(p++) = 255;
//                *(p++) = 255;
//            }
//        }
        glCreateTextures(GL_TEXTURE_2D, 2, tex_);
        glTextureStorage2D(tex_[0], 1, GL_RGBA32F, imageW, imageH);
        glTextureSubImage2D(tex_[0], 0, 0, 0, imageW, imageH, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
        glBindTextureUnit(0, tex_[0]);

        FileUtils::LoadImage("../../Chapter2/res/03.jpg", &imageData, &imageW, &imageH, &channels);
        glTextureStorage2D(tex_[1], 1, GL_RGBA32F, imageW, imageH);
        glTextureSubImage2D(tex_[1], 0, 0, 0, imageW, imageH, GL_RGB, GL_UNSIGNED_BYTE, imageData);
        glBindTextureUnit(1, tex_[1]);

        // create sampler
        glCreateSamplers(2, sampler);

        glSamplerParameteri(sampler[0], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glSamplerParameteri(sampler[0], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glBindSampler(0, sampler[0]);

        glSamplerParameteri(sampler[1], GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glSamplerParameteri(sampler[1], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glBindSampler(1, sampler[1]);

        //2d array texture
//        GLuint arrTex;
//        glCreateTextures(GL_TEXTURE_2D_ARRAY, 1, &arrTex);
//        glTextureStorage3D(arrTex, 8, GL_RGBA8, 256, 256, 100);
//        for(int i = 0; i < 100; ++i)
//        {
//            glTextureSubImage3D(arrTex, 0, 0, 0, i, 256, 256, i, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
//        }

        delete imageData;
    }

    void OnRender() override
    {
        static const GLfloat bgColor[] = {0.2f, 0.2f, 0.2f, 1.0f};
        glClearBufferfv(GL_COLOR, 0, bgColor);

        sp_.Use();

        glBindVertexArray(vao_);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        //glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
//        glDrawElementsBaseVertex()
    }

    void OnDestroy() override
    {
        glDeleteVertexArrays(1, &vao_);
        glDeleteBuffers(1, &ebo_);
        glDeleteBuffers(1, &vbo_);
        glDeleteTextures(2, tex_);
        glDeleteSamplers(2, sampler);
    }

    void OnResize(int w, int h) override
    {
        glViewport(0, 0, w, h);
    }

private:
    GLuint vao_;
    GLuint vbo_;
    GLuint ebo_;
    GLuint tex_[2];
    GLuint sampler[2];
    ShaderProgram sp_;
};

#endif //OPENGLLEARN_TEXTUREAPP_H
